package com.xyy.saas.payment.adpater.impl;

import com.alibaba.druid.support.json.JSONUtils;
import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeCreateModel;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.request.AlipaySystemOauthTokenRequest;
import com.alipay.api.request.AlipayTradeCreateRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.alipay.api.response.AlipayTradeCreateResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.xyy.framework.redis.core.RedisClient;
import com.xyy.saas.payment.adpater.PaymentAdapter;
import com.xyy.saas.payment.callback.core.result.CallbackRunner;
import com.xyy.saas.payment.cashier.core.vo.ChannelLoginVO;
import com.xyy.saas.payment.channel.login.core.bo.ChannelLoginResultBO;
import com.xyy.saas.payment.common.Constants;
import com.xyy.saas.payment.common.exception.PaymentException;
import com.xyy.saas.payment.config.MiniCashierPayConfig;
import com.xyy.saas.payment.config.YkqAliConfig;
import com.xyy.saas.payment.cores.bo.MiniPayBo;
import com.xyy.saas.payment.cores.bo.OrderClosedBo;
import com.xyy.saas.payment.cores.enums.ResultCodeEnum;
import com.xyy.saas.payment.cores.vo.CallbackResultVo;
import com.xyy.saas.payment.dao.model.RefundOrder;
import com.xyy.saas.payment.dao.model.ThirdUserInfo;
import com.xyy.saas.payment.payment.core.bo.RefundBO;
import com.xyy.saas.payment.payment.core.dto.MiniPayDto;
import com.xyy.saas.payment.payment.core.dto.OrderClosedDto;
import com.xyy.saas.payment.payment.core.dto.RefundDto;
import com.xyy.saas.payment.service.ThirdUserInfoService;
import com.xyy.saas.payment.util.ApolloUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @author wangtianqi
 * @date 2020/3/2 15:39
 */
@Service
@DependsOn({"ykqAliConfig","miniCashierPayConfig"})
public class AliAdapter implements PaymentAdapter, InitializingBean {
    private static final Logger LOGGER = LoggerFactory.getLogger(AliAdapter.class);

    @Autowired
    private YkqAliConfig ykqAliConfig;
    @Autowired
    private RedisClient redisClient;
    @Autowired
    private CallbackRunner callbackRunner;
    @Autowired
    private MiniCashierPayConfig miniCashierPayConfig;
    @Resource
    private ThirdUserInfoService thirdUserInfoService;

    public static Map<String, Object> map = new HashMap<>();
    private AlipayClient miniLoginClient;
    private AlipayClient pinganMiniLoginClient;

    @Override
    public ChannelLoginResultBO login(ChannelLoginVO channelLoginVO) throws PaymentException {
        ChannelLoginResultBO bo = new ChannelLoginResultBO();
        AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
        request.setGrantType("authorization_code");
        request.setCode(channelLoginVO.getAuthCode());

        try {
            ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(channelLoginVO.getAccountId());
            // 如果 appID 及 密钥等关键信息不改变 是 线程安全的
            AlipaySystemOauthTokenResponse response =  null;

            if(thirdUserInfo != null && Constants.PINGAN.equals(thirdUserInfo.getThirdType())) {
                response = pinganMiniLoginClient.execute(request);
            }
            // 大商户
            else if(Boolean.parseBoolean(ApolloUtil.getProperty(Constants.EC_POP_BIG_SHOP_TO_PINGAN_SWITCH, Boolean.FALSE.toString())) &&
                    Objects.equals(ApolloUtil.getProperty(Constants.POP_DIRECT_PAY_YBM_TECH_USER_ID), channelLoginVO.getAccountId()))
            {
                response = pinganMiniLoginClient.execute(request);
            }
            else {
                response = miniLoginClient.execute(request);
            }

            if(response.isSuccess()) {
                LOGGER.info("AliAdapter#login success payNo:{} channelLoginVO:{} userId:{}",channelLoginVO.getPayNo(), JSON.toJSONString(channelLoginVO), response.getUserId());
                bo.setChannelUserId(response.getUserId());
                return bo;
            }
            else {
                LOGGER.error("AliAdapter#login failed wrong response payNo:{} channelLoginVO:{} response:{}",channelLoginVO.getPayNo(), JSON.toJSONString(channelLoginVO), JSON.toJSONString(response));
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
            }
        }
        catch(Exception e) {
            LOGGER.error("AliAdapter#login error channelLoginVO:{}", JSON.toJSONString(channelLoginVO), e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }
    }

    @Override
    public RefundBO refund(RefundDto refundDto) throws PaymentException {
        RefundBO refundBO = new RefundBO();
        AlipayTradeRefundResponse response;
        try {
            AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", ykqAliConfig.getAppId(), ykqAliConfig.getPrivateKey(), "json", "UTF-8", ykqAliConfig.getPublickKey(), "RSA2");
            AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
            AlipayTradeRefundModel refundModel = new AlipayTradeRefundModel();
            refundModel.setOutTradeNo(refundDto.getPayNo());
            refundModel.setRefundAmount(new BigDecimal(refundDto.getAmount()).divide(new BigDecimal(100)).toString());
            refundModel.setOutRequestNo(refundDto.getRefundNo());
            refundModel.setRefundReason(refundDto.getDescription());
            request.setBizModel(refundModel);
            response = alipayClient.execute(request);
        } catch (Exception e) {
            LOGGER.error("AliAdapter#refund create failed refundDto:{}", JSON.toJSONString(refundDto), e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }
        if(response.isSuccess()){
            if("10000".equals(response.getCode()) && "Y".equals(response.getFundChange())) {
                refundBO.setRefundNo(refundDto.getRefundNo());
                refundBO.setChannelRefundNo("");
                refundBO.setStatus(RefundOrder.STATUS_FINISH);
                refundBO.setDate(response.getGmtRefundPay());
                Long unixTime = response.getGmtRefundPay().getTime() / 1000;
                LOGGER.info("WxAdapter#refund create success pay_no:{},result:{}", refundDto.getPayNo(), JSON.toJSONString(response));
                callbackRunner.aliRefundCallback(refundDto.getRefundNo(), CallbackResultVo.TYPE_REFUND_SUCCESS, refundDto.getAmount(),0, unixTime, refundDto.getCallbackUrl());
                return refundBO;
            } else {
                LOGGER.error("AliAdapter#refund create failed refundDto:{},result:{}", JSON.toJSONString(refundDto), JSON.toJSONString(response));
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
            }
        } else {
            LOGGER.error("AliAdapter#refund response not success refundDto:{} response:{}", JSONUtils.toJSONString(refundDto), JSONUtils.toJSONString(response));
            if(response.getSubMsg().contains("余额不足")) {
                throw new PaymentException(ResultCodeEnum.CHANNEL_TODAY_MONEY_NOT_ENOUGH);
            }else {
                LOGGER.error("AliAdapter#refund create failed refundDto:{},result:{}", JSON.toJSONString(refundDto), JSON.toJSONString(response));
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
            }
        }
    }

    @Override
    public MiniPayBo miniPay(MiniPayDto miniPayDto) throws PaymentException {
        LOGGER.info("AliAdapter miniPay begin miniPayDto:{}", JSON.toJSON(miniPayDto));
        MiniPayBo miniPayBo = new MiniPayBo();
        //实例化客户端
        AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", ykqAliConfig.getAppId(), ykqAliConfig.getPrivateKey(), "json", "UTF-8", ykqAliConfig.getPublickKey(), "RSA2");
        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.create.
        AlipayTradeCreateRequest request = new AlipayTradeCreateRequest();
        request.setNotifyUrl(ykqAliConfig.getNotifyUrlPay());
        AlipayTradeCreateModel model = new AlipayTradeCreateModel();
        model.setOutTradeNo(miniPayDto.getPayNo());
        model.setTotalAmount(new BigDecimal(miniPayDto.getAmount()).divide(new BigDecimal(100)).toString());
        model.setSubject(miniPayDto.getBody());
        model.setBuyerId(miniPayDto.getMap().get("buyerId").toString());
        model.setTimeoutExpress(miniPayDto.getMap().get("timeoutExpress").toString());
        request.setBizModel(model);
        try {
            //使用的是execute
            AlipayTradeCreateResponse response = alipayClient.execute(request);
            if (!"10000".equals(response.getCode())){
                LOGGER.error("AliAdapter miniPay ali error payNo:{}, code:{}, msg:{}", miniPayDto.getPayNo(), response.getCode(), response.getMsg());
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR.getCode(), ResultCodeEnum.CHANNEL_CONNECT_ERROR.getMsg());
            }
            //获取返回的tradeNO。
            String trade_no = response.getTradeNo();
            miniPayBo.setPayLoad(trade_no);
            LOGGER.info("AliAdapter miniPay end PayNo:{}", miniPayDto.getPayNo());
        } catch (AlipayApiException e) {
            LOGGER.error("AliAdapter miniPay alipay error code:{}, msg:{}, payNo:{}", e.getErrCode(), e.getErrMsg(), miniPayDto.getPayNo());
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR.getCode(), ResultCodeEnum.CHANNEL_CONNECT_ERROR.getMsg());
        }
        redisClient.set(miniPayDto.getBusinessOrderType() + miniPayDto.getPayNo() + "alimini", miniPayBo, 7200);
        return miniPayBo;
    }

    @Override
    public OrderClosedBo orderClosed(OrderClosedDto orderClosedDto) throws PaymentException {
        throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        miniLoginClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", miniCashierPayConfig.getAliAppId(), miniCashierPayConfig.getAliPrivateKey(), "json", "GBK", miniCashierPayConfig.getAliPublicKey(), "RSA2");
        pinganMiniLoginClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do", miniCashierPayConfig.getPinganAliAppId(), miniCashierPayConfig.getPinganAliPrivateKey(), "json", "GBK", miniCashierPayConfig.getPinganAliPublicKey(), "RSA2");
    }
}
